%include {
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //                                                                                                                            //
    //                                                         WNProject                                                          //
    //                                                                                                                            //
    //            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
    //                                                                                                                            //
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    #include "WNMemory/inc/WNMemory.h"
    #include "WNScripting/inc/WNScriptLexerDefines.h"
    #include "WNScripting/inc/WNExpression.h"
    #include "WNScripting/inc/WNInstruction.h"
    #include "WNScripting/inc/WNDataType.h"
    #include "WNScripting/inc/WNTopLevelElement.h"
    #include "WNScripting/inc/WNParseTree.h"
    #include "WNScripting/inc/WNLValue.h"
    #include "WNScripting/inc/WNFunctionHeaderList.h"
    #include "WNScripting/inc/WNExpressionList.h"

    #include <assert.h>
    #include <iostream>
    #include <vector>

    #define WNCREATE_TOKEN(tok, startTok, endTok) \
        tok = (WNScriptToken*)WNMalloc(sizeof(WNScriptToken)); \
        tok->mStartChar = startTok->mStartChar; \
        tok->mEndChar = endTok->mEndChar; \
        tok->mStartLine = startTok->mStartLine; \
        tok->mEndLine = endTok->mEndLine;

    #define WNTOKVAL(tok) tok->mStartLine, tok->mEndLine, tok->mStartChar, tok->mEndChar
    #define WNTWOPARSEVAL(tok1, tok2) tok1->mStartLine, tok2->mEndLine, tok1->mStartChar, tok2->mEndChar

    #pragma warning( disable: 4065 )
    
    using namespace WNMemory;
    using namespace WNScripting;
}

%token_type {WNScriptToken*}
%token_prefix WN_SCRIPT_
%name WNScriptParse
%extra_argument { WNParseTree** pParseTree }

%type expr {WNExpression*}
%type instruction {WNInstruction*}
%type instructionlist {WNInstructionList*}
%type dataType {WNDataType*}
%type toplevelelementlist {WNTopLevelElementList*}
%type toplevelelement {WNTopLevelElement*}
%type program {WNParseTree*}
%type instructionBranch {WNInstruction*}
%type instructionNoBranchNoList {WNInstruction*}
%type instructionNoBranch {WNInstruction*}
%type arrayaccess {WNLValue*}
%type lvalue {WNLValue*}
%type arrayDataType {WNDataType*}
%type structDataType {WNDataType*}
%type arrayInitializer {WNDataType*}
%type scalarDataType {WNDataType*}
%type functionHeaderList {WNFunctionHeaderList*}
%type expressionList {WNExpressionList*}
%type deletebrackets {WN_UINT32}
%type bracketList {std::vector<WNExpression*>*}
%type rVal {WNExpression*}
%type createParams {WN_UINT8}
%type exprComp {WNExpression*}
%type exprNonComp {WNExpression*}
%type exprFuncComp {WNExpression*}
%type stackRet {WN_BOOL}
%type equalType {WNStaticArithmetic}

%left EQUALITY INEQUALITY LESS GREATER LESSTHANOREQUAL GREATERTHANOREQUAL HEAP_ALLOC.
%left PLUS MINUS.   
%left DIVIDE TIMES MOD.
%left COMMA.
%left DOT.
%left STRING COPY.
%left LSQBRACKET RSQBRACKET.
%left STACKRET.
%left PLUS_EQUAL MINUS_EQUAL TIMES_EQUAL DIVIDE_EQUAL MOD_EQUAL.
%right ARRAY_LENGTH.

%default_type {WNScriptToken*}

%syntax_error {  
    fprintf(stderr, "Syntax Error! %d\n", yymajor);
}

program(A) ::= toplevelelement(B). {A = WN_NEW(WNParseTree(WNTOKVAL(B), B)); *pParseTree = A; }

dataType(A) ::= scalarDataType(B). { A = B; }
dataType(A) ::= arrayDataType(B). { A = B; }
dataType(A) ::= structDataType(B). { A = B; }
scalarDataType(A) ::= INTTYPE(B). { A = WN_NEW(WNDataType(WNTOKVAL(B), eWNTypeInt)); WNFree(B); }
scalarDataType(A) ::= FLOATTYPE(B).  { A = WN_NEW(WNDataType(WNTOKVAL(B), eWNTypeFloat)); WNFree(B); }
scalarDataType(A) ::= CHARTYPE(B). { A = WN_NEW(WNDataType(WNTOKVAL(B), eWNTypeChar)); WNFree(B); }

structDataType(A) ::= USERTYPENAME(B). { A = WN_NEW(WNDataType(WNTOKVAL(B), B->mTokenString)); WNFree(B); }
arrayDataType(A) ::= dataType(B) LSQBRACKET(C) RSQBRACKET(D). { A = WN_NEW(WNDataType(WNTWOPARSEVAL(B, D), B)); WNFree(C); WNFree(D); }

bracketList(A) ::= LSQBRACKET(B) exprComp(C) RSQBRACKET(D). { A = WN_NEW(std::vector<WNExpression*>()); A->push_back(C); WNFree(B); WNFree(D); }
bracketList(A) ::= bracketList(B) LSQBRACKET(C) exprComp(D) RSQBRACKET(E). { A = B; A->push_back(D); WNFree(C); WNFree(E); }

createParams(A) ::= HEAP_ALLOC(B) TILDE(C). { A = 0x11; WNFree(B); WNFree(C); }
createParams(A) ::= HEAP_ALLOC(B). { A = 0x01; WNFree(B); }
createParams(A) ::= TILDE(B). { A = 0x10; WNFree(B); }
createParams(A) ::= . { A = 0x00; }

rVal(A) ::= createParams(B) structDataType(C) bracketList(D). { A = WN_NEW(WNExpression(WNTOKVAL(C), C, ((B&0x01) == 0x01), D, ((B&0x10) == 0x10))); }
rVal(A) ::= createParams(B) scalarDataType(C) bracketList(D). { A = WN_NEW(WNExpression(WNTOKVAL(C), C, ((B&0x01) == 0x01), D, WN_FALSE)); }
rVal(A) ::= createParams(B) structDataType(C). { A = WN_NEW(WNExpression(WNTOKVAL(C), C, ((B&0x01) == 0x01), WN_NEW(std::vector<WNExpression*>()), WN_FALSE)); }
rVal(A) ::= createParams(B) STRING(C). { A = WN_NEW(WNExpression(WNTOKVAL(C), C->mStringConst, ((B&0x01) == 0x01))); }

stackRet(A) ::= . { A = false; }
stackRet(A) ::= STACKRET(B). { A = true; WNFree(B); }

toplevelelement(A) ::= stackRet(J) dataType(B) FUNCTION(C) IDENTIFIER(D) LBRACKET(E) RBRACKET(F) LBRACE(G) instructionlist(H) RBRACE(I). { A = WN_NEW(WNTopLevelElement(WNTWOPARSEVAL(B, I), J, B, D->mTokenString, H)); WNFree(C); WNFree(D); WNFree(E); WNFree(F); WNFree(G); WNFree(I); }
toplevelelement(A) ::= stackRet(K) dataType(B) FUNCTION(C) IDENTIFIER(D) LBRACKET(E) functionHeaderList(F) RBRACKET(G) LBRACE(H) instructionlist(I) RBRACE(J). { A = WN_NEW(WNTopLevelElement(WNTWOPARSEVAL(B, I), K, B, D->mTokenString, I, F)); WNFree(C); WNFree(D); WNFree(E); WNFree(G); WNFree(H); WNFree(J); } 

functionHeaderList(A) ::= dataType(B) IDENTIFIER(C). { A = WN_NEW(WNFunctionHeaderList(WNTWOPARSEVAL(B, C), B, C->mTokenString)); WNFree(C); }
functionHeaderList(A) ::= dataType(B) IDENTIFIER(C) COMMA(D) functionHeaderList(E). { A = WN_NEW(WNFunctionHeaderList(WNTWOPARSEVAL(B, E), B, C->mTokenString, E)); WNFree(C); WNFree(D); }

lvalue(A) ::= IDENTIFIER(B). { A = WN_NEW(WNLValue(WNTOKVAL(B), B->mTokenString)); WNFree(B); }
lvalue(A) ::= lvalue(B) LSQBRACKET(C) expr(D) RSQBRACKET(E). { A = WN_NEW(WNLValue(WNTWOPARSEVAL(B, E), B, D));  WNFree(C); WNFree(E); }
lvalue(A) ::= lvalue(B) DOT(C) IDENTIFIER(D). { A = WN_NEW(WNLValue(WNTWOPARSEVAL(B, D), B, D->mTokenString)); WNFree(C); WNFree(D); }

instructionlist(A) ::= instructionlist(B) instruction(C). { A = WN_NEW(WNInstructionList(WNTWOPARSEVAL(B, C), C, B)); }
instructionlist(A) ::= . { A = WN_NEW(WNInstructionList(0, 0, 0, 0)); }

instruction(A) ::= instructionNoBranchNoList(B). { A = B; }
instruction(A) ::= instructionBranch(B). { A = B; }
instruction(A) ::= LBRACE(B) instructionlist(C) RBRACE(D). { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, D), C)); WNFree(B); WNFree(D); }

instructionNoBranch(A) ::= instructionNoBranchNoList(B). { A = B; }
instructionNoBranch(A) ::= LBRACE(B) instructionlist(C) RBRACE(D). { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, D), C)); WNFree(B); WNFree(D); }

instructionNoBranchNoList(A) ::= dataType(B) IDENTIFIER(C) EQUAL(D) expr(E) SEMICOLON(F). { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, F), B, C->mTokenString, WN_NEW(WNLValue(WNTOKVAL(C), C->mTokenString)), E)); WNFree(C); WNFree(D); WNFree(F); }

equalType(A) ::= PLUS_EQUAL(B). { A = eWNStaticPlusEqual; WNFree(B); }
equalType(A) ::= MINUS_EQUAL(B). { A = eWNStaticMinusEqual; WNFree(B); }
equalType(A) ::= TIMES_EQUAL(B). { A = eWNStaticTimesEqual; WNFree(B); }
equalType(A) ::= DIVIDE_EQUAL(B). { A = eWNStaticDivideEqual; WNFree(B); }
equalType(A) ::= MOD_EQUAL(B). { A = eWNStaticModEqual; WNFree(B); }
equalType(A) ::= EQUAL(B). { A = eWNStaticAssign; WNFree(B); }

instructionNoBranchNoList(A) ::= lvalue(B) equalType(C) expr(D) SEMICOLON(E). { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, E), B, C, D)); WNFree(E);}

instructionNoBranchNoList(A) ::= exprFuncComp(B) SEMICOLON(C). { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, C), B, false)); WNFree(C); }
instructionNoBranchNoList(A) ::= SEMICOLON(B). { A = WN_NEW(WNInstruction(WNTOKVAL(B))); WNFree(B); }
instructionNoBranchNoList(A) ::= RETURN(B) expr(C) SEMICOLON(D). { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, D), C, true)); WNFree(B); WNFree(D); }

instructionNoBranchNoList(A) ::= HEAP_DELETE(B) lvalue(C) SEMICOLON(D). { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, C), C, static_cast<WN_UINT32>(0))); WNFree(B); WNFree(D); }
instructionNoBranchNoList(A) ::= HEAP_DELETE(B) LSQBRACKET(C) RSQBRACKET(D) deletebrackets(E) lvalue(F) SEMICOLON(G). { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, C), F, static_cast<WN_UINT32>(E + 1))); WNFree(B); WNFree(C); WNFree(D); WNFree(G); }

deletebrackets(A) ::= . { A = 0; }
deletebrackets(A) ::= LSQBRACKET(B) RSQBRACKET(C) deletebrackets(D) . { A = D + 1; WNFree(B); WNFree(C); }

instructionBranch(A) ::= WHILE(B) LBRACKET(C) expr(D) RBRACKET(E) instructionNoBranch(F). { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, F), D, F, eWNInstructionWhileLoop)); WNFree(B); WNFree(C); WNFree(E); }
instructionBranch(A) ::= DO(B) instructionNoBranch(C) WHILE(D) LBRACKET(E) expr(F) RBRACKET(G). { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, G), F, C, eWNInstructionDoLoop)); WNFree(B); WNFree(D); WNFree(E); WNFree(G); }
instructionBranch(A) ::= IF(B) LBRACKET(C) expr(D) RBRACKET(E) instructionNoBranch(F) ELSE(G) instruction(H). { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, H), D, F, H)); WNFree(B), WNFree(C); WNFree(E); WNFree(G); }
instructionBranch(A) ::= IF(B) LBRACKET(C) expr(D) RBRACKET(E) instructionNoBranch(F).  { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, F), D, F, eWNInstructionConditional)); WNFree(B); WNFree(C); WNFree(E); }
instructionBranch(A) ::= FOR(B) LBRACKET(C) instructionNoBranchNoList(D) expr(E) SEMICOLON(F) instructionNoBranchNoList(G) RBRACKET(H) instruction(I). { A = WN_NEW(WNInstruction(WNTWOPARSEVAL(B, I), D, E, G, I)); WNFree(B); WNFree(C); WNFree(F); WNFree(H); }

exprComp(A) ::= exprComp(B) MINUS(D)  exprComp(C). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B,C), eWNArithmeticSubtract, B, C) ); WNFree(D); }
exprComp(A) ::= exprComp(B) PLUS(D)  exprComp(C). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B,C), eWNArithmeticAdd, B, C) ); WNFree(D); }
exprComp(A) ::= exprComp(B) TIMES(D)  exprComp(C). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B,C), eWNArithmeticMultiply, B, C) ); WNFree(D); }
exprComp(A) ::= exprComp(B) DIVIDE(D) exprComp(C). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B,C), eWNArithmeticDivide, B, C) ); WNFree(D); }
exprComp(A) ::= exprComp(B) MOD(D) exprComp(C).    { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B,C), eWNArithmeticMod, B, C) ); WNFree(D); }
exprComp(A) ::= exprComp(B) EQUALITY(D) exprComp(C). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B, C), B, C, eWNEqual) ); WNFree(D); }
exprComp(A) ::= exprComp(B) INEQUALITY(D) exprComp(C). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B, C), B, C, eWNNotEqual) ); WNFree(D); }
exprComp(A) ::= exprComp(B) LESS(D) exprComp(C). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B, C), B, C, eWNLessThan) ); WNFree(D); }
exprComp(A) ::= exprComp(B) GREATER(D) exprComp(C). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B, C), B, C, eWNGreaterThan) ); WNFree(D); }
exprComp(A) ::= exprComp(B) LESSTHANOREQUAL(D) exprComp(C). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B, C), B, C, eWNLessThanOrEqual) ); WNFree(D); }
exprComp(A) ::= exprComp(B) GREATERTHANOREQUAL(D) exprComp(C). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B, C), B, C, eWNGreaterThanOrEqual) ); WNFree(D); }
exprComp(A) ::= LBRACKET(B) exprComp(C) RBRACKET(D). { A = C; WNFree(B); WNFree(D); }

exprComp(A) ::= ARRAY_LENGTH(B) exprComp(C). { A = WN_NEW(WNExpression(WNTOKVAL(B), C, eWNArrayLength)); WNFree(B); }
exprComp(A) ::= INTEGER(B).    { A = WN_NEW(WNExpression(WNTOKVAL(B),  B->mIntConst, eWNConstantIntExpression)); WNFree(B); }
exprComp(A) ::= FLOAT(B).      { A = WN_NEW(WNExpression(WNTOKVAL(B),  B->mFloatConst, eWNConstantFloatExpression)); WNFree(B); }
exprComp(A) ::= IDENTIFIER(B). { A = WN_NEW(WNExpression(WNTOKVAL(B),  B->mTokenString)); WNFree(B); }
exprComp(A) ::= CHAR(B).       { A = WN_NEW(WNExpression(WNTOKVAL(B),  B->mCharConst, eWNConstantCharExpression)); WNFree(B); }
exprComp(A) ::= exprComp(B) DOT(C) IDENTIFIER(D). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B, D), B, D->mTokenString)); WNFree(D); WNFree(C); }

exprComp(A) ::= exprComp(B) LSQBRACKET(C) exprComp(D) RSQBRACKET(E). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B, E), eWNArithmeticSquareBrackets, B, D)); WNFree(C); WNFree(E); }

exprComp(A) ::= IDENTIFIER(B) LBRACKET(C) RBRACKET(D). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B, D), B->mTokenString, static_cast<WNExpressionList*>(WN_NULL))); WNFree(B); WNFree(C); WNFree(D);}
exprComp(A) ::= exprFuncComp(B). { A = B; }
exprFuncComp(A) ::= IDENTIFIER(B) LBRACKET(C) expressionList(D) RBRACKET(E). { A = WN_NEW(WNExpression(WNTWOPARSEVAL(B, E), B->mTokenString, D)); WNFree(B); WNFree(C); WNFree(E); }

expressionList(A) ::= expr(B). { A = WN_NEW(WNExpressionList(WNTOKVAL(B), B)); }
expressionList(A) ::= expressionList(B) COMMA(C) expr(D). { A = WN_NEW(WNExpressionList(WNTWOPARSEVAL(B, D), B, D)); WNFree(C);}

expr(A) ::= exprComp(B). { A = B; }

expr(A) ::= rVal(B). { A = B; }
